package com.zebra.remoting.protocol;

import com.zebra.remoting.config.ConstantValue;
import com.zebra.remoting.config.StateCode;
import com.zebra.remoting.connection.Connection;
import com.zebra.remoting.tms.TmsSendMessageUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.util.List;

public class ZebraDecoder extends MessageToMessageDecoder<ByteBuf> {

    private static final InternalLogger logger = InternalLoggerFactory.getInstance(ZebraDecoder.class);

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        if (in == null) {
            return;
        }
        if (in.readableBytes() < ConstantValue.HEADER_SIZE) {
            Connection connection = Connection.getByChannel(ctx.channel());
            if (null == connection){
                logger.error("Channel close By: Decoder ByteBuf too small {}", ctx.channel().toString());
                ctx.close().sync();
            }else {
                logger.error("Channel close By: Decoder ByteBuf too small 1: {}  ", connection.toString());
            }
            return;
        }

        if (in.readableBytes() > ConstantValue.MESSAGE_MAX_FRAME_LENGTH) {
            Connection connection = Connection.getByChannel(ctx.channel());
            if (null == connection){
                logger.error("Channel close By: Decoder ByteBuf too big {}", ctx.channel().toString());
                ctx.close().sync();
            }else {
                logger.error("Channel close By: Decoder ByteBuf too big: {}  ", connection.toString() );
            }
            return;
        }

        short flag = in.readShort();
        short len = in.readShort();
        short cmd = in.readShort();
        long token = in.readLong();
        long userId = in.readLong();
        short state = in.readShort();
        short gameId = in.readShort();
        int id = in.readInt();
        short reserve = in.readShort();

        if (flag != (short) (cmd ^ len ^ 1616) || len < 28) //len = cmd+bytes.length
        {
            Connection connection = Connection.getByChannel(ctx.channel());
            if (null == connection){
                logger.error("Decoder ByteBuf parse flag: Channel{}, cmd:{}; length：{}", ctx.channel().toString(), cmd, len);
                ctx.close().sync();
            }else {
                TmsSendMessageUtil.sendStateCode(ctx.channel(), cmd, userId, token, StateCode.InactiveChannel.code());
                logger.error("Decoder ByteBuf parse flag: {}  cmd:{}; length：{}", connection.toString(),   cmd, len);
            }
            return;
        }

        int length = len - 28;
        int realLength = in.readableBytes();
        if (realLength < length) {
            Connection connection = Connection.getByChannel(ctx.channel());
            if (null == connection){
                logger.error("Decoder ByteBuf parse length: Channel{}, cmd:{}; length：{}, len{}", ctx.channel().toString(), cmd, length, realLength);
                ctx.close().sync();
            }else {
                TmsSendMessageUtil.sendStateCode(ctx.channel(), cmd, userId, token, StateCode.InactiveChannel.code());
                logger.error("Decoder ByteBuf parse length: {}  cmd:{}; length：{}, len{}", connection.toString(),   cmd, length, realLength);
            }
            return;
        }

        ZebraCommand context = ZebraCommand.builder()
                .cmd(cmd)
                .id(id)
                .state(state)
                .token(token)
                .userId(userId)
                .gameId(gameId)
                .reserve(reserve)
                .build();

        if (realLength != length){
            logger.error("Decoder ByteBuf parse： realLength {}, length {}", realLength, length);
            ByteBuf buf = in.readBytes(length);
            readRequest(context, buf);
            buf.release();
        }else {
            readRequest(context, in);
        }

        if (null == context.getCommandModel()) {
            Connection connection = Connection.getByChannel(ctx.channel());
            if (null == connection){
                logger.error("Decoder Channel close By: Decoder Server could not execute the command! {} {}", context.toString(), ctx.channel().toString());
                ctx.close().sync();
            }else {
                TmsSendMessageUtil.sendStateCode(ctx.channel(), cmd, userId, token, StateCode.InactiveChannel.code());
                logger.error("Decoder CommandModel is null: Decoder Server could not execute the command! {} {} ", connection.toString(),  context.toString());
            }
            return;
        }
        out.add(context);
    }

    private void readRequest(ZebraCommand request, ByteBuf buf){
        CommandModel commandModel = CommandModelManager.getCommandModel(request.getCmd());
        if (null == commandModel || null == commandModel.getRequest()){
            return;
        }
        request.setCommandModel(commandModel);
        request.setRequest(commandModel.getRequest().read(buf));
    }

}
